package impl

import (
	"context"
	"fmt"

	"gitlab.com/go-course-project/go15/devcloud-mini/cmdb/apps/resource"
	"gitlab.com/go-course-project/go15/devcloud-mini/cmdb/apps/secret"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo/options"

	"github.com/infraboard/mcube/v2/types"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
	lighthouse "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/lighthouse/v20200324"

	"github.com/infraboard/mcube/v2/ioc/config/cache"
)

func (i *impl) CreateSecret(ctx context.Context, in *secret.CreateSecretRequest) (*secret.Secret, error) {

	ins := secret.NewSecret(in)

	// 需要加密
	if err := ins.EncryptedApiSecret(); err != nil {
		return nil, err
	}

	_, err := i.col.UpdateOne(ctx, bson.M{"_id": ins.Id}, bson.M{"$set": in}, options.Update().SetUpsert(true))
	if err != nil {
		return nil, err
	}
	return ins, nil
}

func (i *impl) QuerySecret(ctx context.Context, in *secret.QuerySecretRequest) (*types.Set[*secret.Secret], error) {
	set := secret.NewSecretSet()

	filter := bson.M{}
	opt := options.Find()
	opt.SetLimit(int64(in.PageSize))
	opt.SetSkip(in.ComputeOffset())
	cursor, err := i.col.Find(ctx, filter, opt)
	if err != nil {
		return nil, err
	}

	for cursor.Next(ctx) {
		e := secret.NewSecret(&secret.CreateSecretRequest{})
		if err := cursor.Decode(e); err != nil {
			return nil, err
		}
		set.Add(e)
	}

	return set, nil
}

// 这个逻辑比较号时, 请求结果缓存 30s
// 缓存命中 id ---> redis.get(”)   json --> unmarshal --> return obj
// 缓存未命中 id ---> redis.get(”)  DescribeSecret() --> obj ----> redis.set(obj) ----> return obj
// 选择一个redis 库
// val, err := rdb.Get(ctx, "key").Result()
// fmt.Println(val)

func (i *impl) DescribeSecret(ctx context.Context, in *secret.DescribeSecretRequeset) (*secret.Secret, error) {
	// 封装过后的改良版
	ins := secret.NewSecret(secret.NewCreateSecretRequest())
	err := cache.NewGetter(ctx, func(ctx context.Context, objectId string) (any, error) {
		return i.describeSecret(ctx, in)
	}).Get(in.Id, ins)
	if err != nil {
		return nil, err
	}
	return ins, nil
	// i.redis.Exists(ctx, in.Id)
	// i.redis.Get(in.Id) // Key Err: Not Exist
	// return obj

	// i.DescribeSecret(ctx, in)
	// i.redis.Set(ctx, ins)
	// return obj
	// return nil, nil
}

// @cached(ttl=30s)
func (i *impl) describeSecret(ctx context.Context, in *secret.DescribeSecretRequeset) (*secret.Secret, error) {
	// 取出后，需要解密
	e := secret.NewSecret(&secret.CreateSecretRequest{})
	if err := i.col.FindOne(ctx, bson.M{"_id": in.Id}).Decode(e); err != nil {
		return nil, err
	}

	e.SetisEncrypted(true)
	if err := e.DecryptedApiSecret(); err != nil {
		return nil, err
	}

	return e, nil
}

// 怎么API怎么设计
// 同步阿里云所有资源, 10分钟，30分钟 ...
// 这个接口调用持续30分钟...
// Req ---> <---- Resp:   能快速响应的同步调用
// Stream API
func (i *impl) SyncResource(ctx context.Context, in *secret.SyncResourceRequest, h secret.SyncResourceHandleFunc) error {
	// 1. 获取Secret
	s, err := i.DescribeSecret(ctx, secret.NewDescribeSecretRequeset(in.Id))
	if err != nil {
		return err
	}

	// 初始化一个云商的SDK, 访问云商的资源
	// 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
	// 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性。以下代码示例仅供参考，建议采用更安全的方式来使用密钥，请参见：https://cloud.tencent.com/document/product/1278/85305
	// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
	credential := common.NewCredential(
		s.ApiKey,
		s.ApiSecret,
	)
	// 实例化一个client选项，可选的，没有特殊需求可以跳过
	cpf := profile.NewClientProfile()
	cpf.HttpProfile.Endpoint = "lighthouse.tencentcloudapi.com"

	for _, region := range s.Regions {
		// 实例化要请求产品的client对象,clientProfile是可选的
		client, _ := lighthouse.NewClient(credential, region, cpf)

		// 实例化一个请求对象,每个接口都会对应一个request对象
		request := lighthouse.NewDescribeInstancesRequest()

		// 返回的resp是一个DescribeInstancesResponse的实例，与请求对象对应
		response, err := client.DescribeInstances(request)
		if err != nil {
			return err
		}
		i.log.Debug().Msgf("raw response: %s", response.ToJsonString())
		// 输出json格式的字符串回包

		// 把返回的对象转化为 ---> Resource
		for index := range response.Response.InstanceSet {
			res := i.TransfterLighthouseInstance(response.Response.InstanceSet[index])

			// 保持
			if _, err := resource.GetService().Save(ctx, res); err != nil {
				i.log.Error().Msgf("save resource %s error, %s", res.Id, err)
				h(secret.ResourceResponse{
					Success: false,
					Message: err.Error(),
				})
				continue
			}

			// 返回
			h(secret.ResourceResponse{
				Success: true,
				Message: fmt.Sprintf("%s 同步成功", res.GetFirstPrivateAddress()),
			})
		}
	}
	return nil
}
